﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using DevExpress.XtraEditors.Repository;
using DevExpress.Utils;
using DevExpress.XtraEditors;
using System.Data;
using System.Drawing;
using DevExpress.XtraEditors.Controls;
using System.IO;
using System.Xml;
using ProgNetComponentsDx.Layout;
using DevExpress.Data.Mask;

namespace System
{
    public static class BaseEditExt
    {
        private static object CustomSourceObj = new object();

        public static void AppendText(this MemoEdit edit, string text)
        {
            if (edit.InvokeRequired)
            {
                edit.Invoke(new Action<MemoEdit, string>(AppendText), edit, text);
                return;
            }
            foreach (Control c in edit.Controls)
            {
                if (c is TextBox)
                {
                    ((TextBox)(c)).AppendText(text);
                    break;
                }
            }
        }
        public static void AppendLine(this MemoEdit edit, string text)
        {
            if (edit.InvokeRequired)
            {
                edit.Invoke(new Action<MemoEdit, string>(AppendLine), edit, text);
                return;
            }
            foreach (Control c in edit.Controls)
            {
                if (c is TextBox)
                {
                    if (c.Text.IsNotNull())
                        text = Constants.NewLineLinux + text;
                    ((TextBox)(c)).AppendText(text);
                    break;
                }
            }
        }
        #region CustomPopup
        private const string CustomPopupSourceName = "ProgNetCustomPopupSource";

        public static void SetCustomPopupSource(this BaseEdit control, List<string> list)
        {
            SetCustomPopupSource(control, list, Keys.None);
        }
        public static void SetCustomPopupSource(this BaseEdit control, List<string> list, Keys popupKey)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("_");
            foreach (string s in list)
                dt.Rows.Add(s);
            SetCustomPopupSource(control, dt, "_", "_", false, popupKey);
        }

        public static void SetCustomPopupSource(this BaseEdit control, DataTable dt, string ValueMember, string DisplayMember, Keys popupKey)
        {
            SetCustomPopupSource(control, dt, ValueMember, DisplayMember, false, popupKey);
        }
        public static void SetCustomPopupSource(this BaseEdit control, DataTable dt, string ValueMember, string DisplayMember)
        {
            SetCustomPopupSource(control, dt, ValueMember, DisplayMember, false, Keys.None);
        }
        public static void SetCustomPopupSource(this BaseEdit control, DataTable dt, string ValueMember, string DisplayMember, bool showHeader, Keys popupKey)
        {
            LookUpEdit le = null;
            if (!control.Controls.ContainsKey(CustomPopupSourceName))
            {
                le = new LookUpEdit();
                le.Name = CustomPopupSourceName;
                le.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder;
                le.Properties.AutoHeight = false;
                le.Width = control.Width;
                le.Height = 0;
                le.Location = new Point(0, 0);
                le.Dock = DockStyle.Bottom;
                control.KeyDown -= cb_KeyDown;
                control.KeyDown += new KeyEventHandler(cb_KeyDown);
                le.Tag = new object[] { control, popupKey };
                le.EditValueChanged += new EventHandler(cb_EditValueChanged);
                control.Controls.Add(le);
                le.SendToBack();
            }
            else
            {
                le = control.Controls[CustomPopupSourceName] as LookUpEdit;
            }
            if (le != null)
            {
                le.Properties.ShowHeader = showHeader;
                le.Properties.LockEvents();
                le.EditValue = null;
                le.Properties.ValueMember = ValueMember;
                le.Properties.DisplayMember = DisplayMember;
                le.Properties.Columns.Clear();
                le.Properties.DataSource = dt;
                le.Properties.PopulateColumns();
                le.Properties.UnLockEvents();
            }
        }

        public static void SetCustomPopupButton(this ButtonEdit control, List<string> list)
        {
            SetCustomPopupButton(control, list, Keys.None);
        }
        public static void SetCustomPopupButton(this ButtonEdit control, List<string> list, Keys popupKey)
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("_");
            foreach (string s in list)
                dt.Rows.Add(s);
            SetCustomPopupButton(control, dt, "_", "_", false, popupKey);
        }

        public static void SetCustomPopupButton(this ButtonEdit control, DataTable dt, string ValueMember, string DisplayMember, Keys popupKey)
        {
            SetCustomPopupButton(control, dt, ValueMember, DisplayMember, false, popupKey);
        }
        public static void SetCustomPopupButton(this ButtonEdit control, DataTable dt, string ValueMember, string DisplayMember)
        {
            SetCustomPopupButton(control, dt, ValueMember, DisplayMember, false, Keys.None);
        }
        public static void SetCustomPopupButton(this ButtonEdit control, DataTable dt, string ValueMember, string DisplayMember, bool showHeader, Keys popupKey)
        {
            LookUpEdit le = null;
            if (!control.Controls.ContainsKey(CustomPopupSourceName))
            {
                EditorButton b = new EditorButton(ButtonPredefines.Combo);
                b.Tag = CustomSourceObj;
                control.Properties.Buttons.Add(b);
                control.Properties.ButtonClick += new ButtonPressedEventHandler(Properties_ButtonClick);
                le = new LookUpEdit();
                le.Name = CustomPopupSourceName;
                le.BorderStyle = DevExpress.XtraEditors.Controls.BorderStyles.NoBorder;
                le.Properties.AutoHeight = false;
                le.Width = control.Width;
                le.Height = 0;
                le.Location = new Point(0, 0);
                le.Dock = DockStyle.Bottom;
                control.KeyDown -= cb_KeyDown;
                control.KeyDown += new KeyEventHandler(cb_KeyDown);
                le.Tag = new object[] { control, popupKey };
                le.EditValueChanged += new EventHandler(cb_EditValueChanged);
                control.Controls.Add(le);
                le.SendToBack();
            }
            else
            {
                le = control.Controls[CustomPopupSourceName] as LookUpEdit;
            }
            if (le != null)
            {
                le.Properties.ShowHeader = showHeader;
                le.Properties.LockEvents();
                le.EditValue = null;
                le.Properties.ValueMember = ValueMember;
                le.Properties.DisplayMember = DisplayMember;
                le.Properties.Columns.Clear();
                le.Properties.DataSource = dt;
                le.Properties.PopulateColumns();
                le.Properties.UnLockEvents();
            }

        }

        static void Properties_ButtonClick(object sender, ButtonPressedEventArgs e)
        {
            BaseEdit edit = sender as BaseEdit;
            if (edit != null && e.Button != null && e.Button.Tag == CustomSourceObj)
            {
                ShowCustomPopup(edit);
            }
        }

        public static void ShowCustomPopup(this BaseEdit control)
        {
            PopupBaseEdit cb = control.Controls[CustomPopupSourceName] as PopupBaseEdit;
            if (cb != null)
            {
                cb.EditValue = control.EditValue;
                cb.Focus();
                cb.ShowPopup();
            }
        }

        private static void cb_KeyDown(object sender, KeyEventArgs e)
        {
            BaseEdit edit = sender as BaseEdit;
            if (edit != null)
            {
                PopupBaseEdit cb = edit.Controls[CustomPopupSourceName] as PopupBaseEdit;
                if (cb != null && !cb.Properties.IsLockUpdate && cb.Tag != null)
                {
                    object[] obj = cb.Tag as object[];
                    if (obj != null && obj[1] is Keys)
                    {
                        Keys k = (Keys)(obj[1]);
                        if (k != Keys.None && e.KeyCode == k && !edit.Properties.ReadOnly && edit.Enabled)
                            ShowCustomPopup(edit);
                    }
                }
            }
        }
        private static void cb_EditValueChanged(object sender, EventArgs e)
        {
            PopupBaseEdit cb = sender as PopupBaseEdit;
            if (cb != null && !cb.Properties.IsLockUpdate && cb.Tag != null)
            {
                object[] obj = cb.Tag as object[];
                if (obj != null && obj[0] is BaseEdit)
                {
                    ((BaseEdit)(obj[0])).EditValue = cb.EditValue;
                }
            }
        }
        #endregion

        #region AutoComplete
        public static void SetAutoCompleteSource(this TextEdit edit, AutoCompleteMode mode, AutoCompleteStringCollection source)
        {
            object o = edit.GetPropertyValue("MaskBox");
            DevExpress.XtraEditors.Mask.MaskBox maskBox = o as DevExpress.XtraEditors.Mask.MaskBox;
            if (maskBox != null)
            {
                maskBox.AutoCompleteSource = AutoCompleteSource.CustomSource;
                maskBox.AutoCompleteMode = mode;
                maskBox.AutoCompleteCustomSource = source;
            }
        }
        public static void SetAutoCompleteSource(this TextEdit edit, AutoCompleteMode mode, List<string> source)
        {
            AutoCompleteStringCollection s = new AutoCompleteStringCollection();
            s.AddRange(source.ToArray());
            SetAutoCompleteSource(edit, mode, s);
        }
        public static void SetAutoCompleteSource(this TextEdit edit, AutoCompleteStringCollection source)
        {
            SetAutoCompleteSource(edit, AutoCompleteMode.Suggest, source);
        }
        public static void SetAutoCompleteSource(this TextEdit edit, List<string> source)
        {
            SetAutoCompleteSource(edit, AutoCompleteMode.Suggest, source);
        }
        public static void SetAutoCompleteSource(this TextEdit edit, AutoCompleteMode mode, AutoCompleteSource source)
        {
            object o = edit.GetPropertyValue("MaskBox");
            DevExpress.XtraEditors.Mask.MaskBox maskBox = o as DevExpress.XtraEditors.Mask.MaskBox;

            if (maskBox != null)
            {
                maskBox.AutoCompleteMode = mode;
                maskBox.AutoCompleteSource = source;
            }
        }
        public static void SetAutoCompleteSource(this TextEdit edit, AutoCompleteSource source)
        {
            SetAutoCompleteSource(edit, AutoCompleteMode.Suggest, source);
        }
        #endregion

        #region Null Input
        public static void PrepareRepositoryForNullInput(this RepositoryItem item)
        {
            item.SetPropertyValue("AllowNullInput", DefaultBoolean.True);
            item.KeyDown += new KeyEventHandler(item_KeyDown);
        }
        public static void PrepareBaseEditForNullInput(this BaseEdit item)
        {
            item.Properties.SetPropertyValue("AllowNullInput", DefaultBoolean.True);
            item.KeyDown += new KeyEventHandler(item_KeyDown);
        }

        private static void item_KeyDown(object sender, KeyEventArgs e)
        {
            BaseEdit edit = (sender as BaseEdit);
            if (edit != null && !edit.Properties.ReadOnly)
            {
                if (e.KeyCode == Keys.Delete)
                {
                    edit.Properties.KeyDown -= item_KeyDown;
                    KeyEventArgs kv = new KeyEventArgs(Keys.Delete | Keys.Control);
                    e.Handled = true;
                    edit.SendKey(kv);
                    edit.Properties.KeyDown += item_KeyDown;
                }
            }
        }
        #endregion

        #region DateEdit
        public static void FocusPart(this DateEdit edit, DatePart part)
        {
            try
            {
                if (edit != null)
                {
                    if (edit.MaskBox != null)
                    {
                        DevExpress.Data.Mask.DateTimeMaskManager Manager = edit.MaskBox.GetFieldValue("_manager") as DevExpress.Data.Mask.DateTimeMaskManager;
                        edit.Focus();
                        edit.Select(0, 1);
                        if (Manager != null)
                        {
                            Manager.CursorHome(true);
                            DateTimeMaskFormatInfo fFormatInfo = Manager.GetFieldValue("fFormatInfo") as DateTimeMaskFormatInfo;
                            if (fFormatInfo == null)
                                return;
                            Type t = typeof(DevExpress.Data.Mask.DateTimeMaskFormatElement_Year);
                            Type t2 = typeof(DevExpress.Data.Mask.DateTimeMaskFormatElement_H24);
                            switch (part)
                            {
                                case DatePart.Year:
                                    break;
                                case DatePart.Month:
                                    t = typeof(DevExpress.Data.Mask.DateTimeMaskFormatElement_Month);
                                    break;
                                case DatePart.Day:
                                    t = typeof(DevExpress.Data.Mask.DateTimeMaskFormatElement_d);
                                    break;
                                case DatePart.Hour:
                                    t = typeof(DevExpress.Data.Mask.DateTimeMaskFormatElement_h12);
                                    break;
                                case DatePart.Minute:
                                    t = typeof(DevExpress.Data.Mask.DateTimeMaskFormatElement_Min);
                                    break;
                                case DatePart.Second:
                                    t = typeof(DevExpress.Data.Mask.DateTimeMaskFormatElement_s);
                                    break;
                                case DatePart.Milisecond:
                                    t = typeof(DevExpress.Data.Mask.DateTimeMaskFormatElement_Millisecond);
                                    break;
                                default:
                                    break;
                            }
                            DevExpress.Data.Mask.DateTimeMaskFormatElement el;
                            int pos = 0;
                            DateTime dt = Manager.GetPropertyValue("NonEmptyCurrentValue").AsDateTime(DateTime.MinValue);

                            for (int i = 0; i < fFormatInfo.Count; i++)
                            {
                                el = fFormatInfo[i] as DevExpress.Data.Mask.DateTimeMaskFormatElement;
                                if (el != null)
                                {
                                    if (el.GetType() == t)
                                    {
                                        edit.Select(pos, 0);
                                        return;
                                    }
                                    else if (part == DatePart.Hour && el.GetType() == t2)
                                    {
                                        edit.Select(pos, 0);
                                        return;
                                    }
                                    pos += el.Format(dt).Length;
                                }
                                Manager.CursorRight(true);
                            }
                            Manager.CursorHome(true);
                        }
                    }
                }
            }
            catch { }
        }

        #endregion

        private static Dictionary<Control, LayoutInfo> layouts = new Dictionary<Control, LayoutInfo>();
        private static Dictionary<Control, LayoutInfo> layouts2 = new Dictionary<Control, LayoutInfo>();
        private class LayoutInfo
        {
            public LayoutInfo(string xml, SplitContainerLayoutSettings settings)
            {
                this.xml = xml;
                this.settings = settings;
            }
            public string xml
            {
                get;
                set;
            }
            public SplitContainerLayoutSettings settings
            {
                get;
                set;
            }
            public bool Lock { get; set; }
        }

        public static string GetLayoutXml(this SplitContainerControl control)
        {
            string layout = "";
            try
            {
                XmlDocument doc = new XmlDocument();
                XmlElement main = doc.CreateElement("Layout");
                main.AppendAttribute("CollapsePanel", control.CollapsePanel);
                main.AppendAttribute("Collapsed", control.Collapsed);
                main.AppendAttribute("SplitterPosition", control.SplitterPosition);
                main.AppendAttribute("FixedPanel", control.FixedPanel);
                main.AppendAttribute("PanelVisibility", control.PanelVisibility);
                main.AppendAttribute("Horizontal", control.Horizontal);
                if (control is DevExpress.XtraGrid.GridSplitContainer)
                    main.AppendAttribute("IsSplitViewVisible", ((DevExpress.XtraGrid.GridSplitContainer)control).IsSplitViewVisible);

                #region create xml
                doc.AppendChild(main);

                StringWriter strWriter = new StringWriter();
                XmlTextWriter xmlWriter = new XmlTextWriter(strWriter);
                xmlWriter.Formatting = Formatting.Indented;
                doc.WriteTo(xmlWriter);
                #endregion

                layout = strWriter.GetStringBuilder().ToString();
            }
            catch (Exception ex) { ex.LogInternal(); }
            return layout;
        }
        public static void SetLayoutXml(this SplitContainerControl control, string xml, SplitContainerLayoutSettings settings = null)
        {
            try
            {
                if (!layouts.ContainsKey(control))
                {
                    control.SplitterPositionChanged += new EventHandler(control_SplitterPositionChanged);
                    control.Disposed += new EventHandler(control_Disposed);
                    layouts.Add(control, new LayoutInfo(xml, settings));

                    if (!control.Created)
                    {
                        control.Invalidated += new InvalidateEventHandler(control_Invalidated);
                        return;
                    }
                }
                if (control.Created)
                {
                    control.Invalidated -= new InvalidateEventHandler(control_Invalidated);
                }
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xml);
                XmlElement main = doc.SelectSingleNode("Layout") as XmlElement;
                if (main != null)
                {
                    if (settings == null || settings.AllowCollapsePanelChange)
                        control.CollapsePanel = control.CollapsePanel.FromObject(main.GetAttribute("CollapsePanel"));

                    control.Collapsed = main.GetAttribute("Collapsed").AsBool();

                    if (settings == null || settings.AllowHorizontalChange)
                        control.Horizontal = main.GetAttribute("Horizontal").AsBool();

                    if (settings == null || settings.AllowFixedPanelChange)
                        control.FixedPanel = control.FixedPanel.FromObject(main.GetAttribute("FixedPanel"));

                    if (settings == null || settings.AllowPanelVisibilityChange)
                        control.PanelVisibility = control.PanelVisibility.FromObject(main.GetAttribute("PanelVisibility"));

                    control.SplitterPosition = main.GetAttribute("SplitterPosition").AsInt();
                    if (control.SplitterPosition != main.GetAttribute("SplitterPosition").AsInt())
                        control.SetFieldValue("splitterPosition", main.GetAttribute("SplitterPosition").AsInt()); //required if panel was not visible

                    if (control is DevExpress.XtraGrid.GridSplitContainer)
                    {
                        if (main.GetAttribute("IsSplitViewVisible").AsBool())
                            ((DevExpress.XtraGrid.GridSplitContainer)(control)).ShowSplitView();
                        else
                            ((DevExpress.XtraGrid.GridSplitContainer)(control)).HideSplitView();
                    }

                    if (layouts.ContainsKey(control))
                        layouts[control].Lock = true;
                    control.Invalidate();
                    if (layouts.ContainsKey(control))
                        layouts[control].Lock = false;
                }
            }
            catch (Exception ex) { ex.LogInternal(); }
        }

        static void control_SplitterPositionChanged(object sender, EventArgs e)
        {
            if (new System.Diagnostics.StackTrace().GetFrame(3).GetMethod().Name == "UpdateDockPosition")
            {
                ProcessControl(sender, true);
                ProcessControl(sender, false);
            }
        }

        private static void ProcessControl(object sender, bool setLayout)
        {
            SplitContainerControl control = sender as SplitContainerControl;
            if (control != null && layouts.ContainsKey(control))
            {
                if (setLayout)
                    control.SetLayoutXml(layouts[control].xml, layouts[control].settings);
                else
                    layouts.Remove(control);
            }
        }
        static void control_Invalidated(object sender, InvalidateEventArgs e)
        {
            if (layouts.ContainsKey(sender as Control))
                if (layouts[sender as Control].Lock)
                    return;
            ProcessControl(sender, true);
        }

        static void control_Disposed(object sender, EventArgs e)
        {
            ProcessControl(sender, false);
            SplitContainerControl control = sender as SplitContainerControl;
            control.Invalidated -= new InvalidateEventHandler(control_Invalidated);
            control.Disposed -= new EventHandler(control_Disposed);
            control.SplitterPositionChanged -= new EventHandler(control_SplitterPositionChanged);
        }

        public static void ReadValueFromBinding(this BaseEdit edit)
        {
            if (edit.DataBindings["EditValue"] != null)
            {
                edit.DataBindings["EditValue"].ReadValue();
            }
        }

        /// <summary>
        /// Gets Edit value if control was created or binding value if control was not created and has binding for EditValue
        /// </summary>
        /// <param name="edit"></param>
        /// <returns></returns>
        public static object EditValueOrBindingValue(this BaseEdit edit)
        {
            object obj = null;
            if (edit != null)
            {
                obj = edit.EditValue;
                if (!edit.Created && edit.DataBindings["EditValue"] != null)
                {
                    Binding b = edit.DataBindings["EditValue"];
                    object val = null;

                    object bindToObject = b.GetFieldValue("bindToObject");
                    if (bindToObject != null)
                    {
                        if (bindToObject.TryRunMethod("GetValue", out val))
                        {
                            if (b.TryRunMethod("FormatObject", out val, val))
                            {
                                return val;
                            }
                        }
                    }
                    if (b.TryRunMethod("GetPropValue", out val))
                    {
                        return val;
                    }
                }
            }
            return obj;
        }

        public static void ForceEditValueChanging(this LookUpEdit edit, ChangingEventArgs e)
        {
            if (!edit.GetPropertyValue("IsMaskBoxUpdate").AsBool())
                edit.Properties.RunMethod("RaiseEditValueChanging", e);
            if (e.Cancel)
            {
                edit.RunMethod("OnCancelEditValueChanging");
            }
            else
            {
                if (edit.RunMethod("CompareEditValue", edit.EditValue, e.NewValue, false).AsBool())
                    return;
                if (!edit.IsModified)
                    edit.SetFieldValue("fOldEditValue", edit.EditValue);
                edit.SetFieldValue("fEditValue", e.NewValue);
                if (edit.IsEditorActive || edit.GetPropertyValue("IsAcceptingEditValue").AsBool())
                    edit.IsModified = true;
                edit.RunMethod("OnEditValueChanged");
                if (!edit.AutoSizeInLayoutControl)
                    return;
                edit.RunMethod("RaiseSizeableChanged");
            }
        }
    }

    public enum DatePart
    {
        Year,
        Month,
        Day,
        Hour,
        Minute,
        Second,
        Milisecond
    }
}
